Add FP8×INT4 rowwise GEMM for ROCm (f8i4bf16_rowwise) - #495
Closed
liligwu wants to merge 2 commits into
Closed
Conversation
* Extend _bf16i4_rowwise_kernel with HAS_X_SCALE constexpr for FP8 activations * Add per-row activation scale epilogue, deferred BF16 upcast, FP8 autotuner pruning * New f8i4bf16_rowwise_gemm.py wrapper: FP8 byte-split, reinterpret, kernel call * Register mslk::f8i4bf16_rowwise torch op on ROCm via torch.library.impl * Add TritonFP8Int4Rowwise benchmark class and stub schemas for python_only mode
|
@q10 has imported this pull request. If you are a Meta employee, you can view this in D116965755. |
…ion. Co-authored-by: Cursor <cursoragent@cursor.com>
meta-codesync Bot
pushed a commit
that referenced
this pull request
Aug 25, 2026
Summary: Pull Request resolved: #502 NOTE: No linked task. Please associate a task with this diff. Adds a ROCm Triton implementation of `mslk::f8i4bf16_rowwise` for FP8 activations and packed INT4 weights while reusing the existing BF16×INT4 kernel. - Defines the operator schema on both CUDA and ROCm so the HIP Python implementation can register reliably. - Specializes the shared kernel with `HAS_X_SCALE`, two BF16 dot operations for FP8-backed inputs, and fused per-row activation scaling. - Adds FP8-specific autotune candidates and pruning while preserving the prior BF16 tuning space. - Validates tensor shapes, dtypes, devices, grouping invariants, and contiguous scale layouts. - Avoids allocating unused split-K workspace for large-M shapes. - Registers the ROCm benchmark implementation and adds a ROCm correctness/dispatch regression test. Upstream PR: #495 Reviewed By: jwfromm Differential Revision: D116965755 fbshipit-source-id: 9d790e148a8e400dbfa1a3f41be627ca140aa8b1
Contributor
|
Merged in #502 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR is a copy of apicciau#3
Summary
Adds a Triton implementation of
f8i4bf16_rowwise(FP8 activations × INT4 weights → BF16 output) for AMD MI300X/MI350X. The kernel reuses the unified_bf16i4_rowwise_kernelfrom the BF16×INT4 path, gated on a compile-timeHAS_X_SCALEconstexpr — no kernel fork.Context
rocm/bf16i4-shuffled-grouped) — this PR stacks on top of that branchImplementation
Shared kernel with HAS_X_SCALE constexpr. The existing
_bf16i4_rowwise_kernelgains two new parameters:x_scale_ptr(per-row FP8 activation dequant scale) andHAS_X_SCALE: tl.constexpr = False. WhenHAS_X_SCALE=False(BF16 path), the kernel behaves identically to before — the default value preserves backward compatibility and the dummy pointer is never dereferenced.Deferred BF16 upcast. The
.to(tl.bfloat16)cast on activation loads is moved from the load site to a single centralised location after the four masking branches. This is required because FP8 inputs return a native FP8 tensor type fromtl.loadthat must be upcast explicitly — but for BF16 inputs the cast is a no-op, so both paths share the same code.Two-dot path for FP8. When
HAS_X_SCALE=True, the kernel issues two separatetl.dotcalls (one for even-K, one for odd-K) instead of fusing viatl.cat. Thetl.catfusion concatenates tiles in LDS before the dot, which causes bank conflicts that are a net loss for FP8 activations on MI300X. Two separate dots avoid the LDS staging at the cost of two MFMA instructions — a net win for this dtype. The BF16 path retains the fusedtl.catdot (controlled byFUSE_DOT).Per-row activation scale epilogue. After the K-loop, the float32 accumulator is multiplied element-wise by
x_scale[m]before storing to the workspace. This fuses the FP8 dequantisation scale into the GEMM epilogue rather than requiring a separate pointwise kernel.FP8 autotuner pruning. Four additional prune rules fire only when
HAS_X_SCALE=True: skipBLOCK_N < 128for large N, skipBLOCK_K > 64, skipSPLIT_K == 2, fixGROUP_SIZE_M = 4. These were determined by profiling winning configs on MI350X and reduce the FP8 autotuning search space by ~5×.Thin Python wrapper.
f8i4bf16_rowwise_gemm.pybyte-splits FP8 activations into even/odd K columns, reinterprets them as FP8TensorWrappers viareinterpret_fp8_type, and calls the shared kernel withHAS_X_SCALE=True. The wrapper also coerces scale/zp tensors to float32. Registered asmslk::f8i4bf16_rowwiseon ROCm viatorch.library.impl.Testing
All 9 existing BF16×INT4 accuracy tests pass (no regressions):
FP8×INT4 accuracy against dequantised reference (
deq(FP8_x) @ deq(INT4_w)):FP8×INT4 dispatch test (
torch.ops.mslk.f8i4bf16_rowwisevs direct call): bitwise identical on all shapes.Performance
Hardware: AMD Instinct MI350X (gfx950), ROCm 7.1.1, Triton 3.5.1+rocm7.1.1.
triton.testing.do_bench, warmup=25, rep=200.Peak: 4600 TFLOPS FP8, 2300 TFLOPS BF16, 8000 GB/s HBM.
f8i4bf16_rowwise vs bf16i4bf16_rowwise — decode shapes (memory-bandwidth bound)
Model-attributed shapes. FP8 and BF16 paths are both kernel-launch-latency bound at small M; performance converges.
f8i4bf16_rowwise vs bf16i4bf16_rowwise — prefill shapes (compute bound)
FP8 MFMA has 2× the throughput of BF16 MFMA. The FP8 path is 5–8% faster at large M.